// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Aliaksei Makarau

#include "common.h"
#include "rotor-light.hpp"

#include "xparameters.h"
#include "xgpio.h"

#define GPIO_OUTPUT_DEVICE_ID	XPAR_GPIO_0_DEVICE_ID
#define LED_CHANNEL           1

namespace rl = rotor_light;

namespace message {

struct BlinkCommand: rl::Message {
  static constexpr auto type_id = __LINE__;
  using rl::Message::Message;
  rl::MessageTypeId get_type_id() const override { return type_id; }
};

} // namespace message

XGpio xil_gpio;
uint32_t gpio_flag = 0xFFFF;

struct Blinker : rl::Actor<2> {
  using Parent = Actor<2>;

  void initialize() override {
    subscribe(&Blinker::on_blink_command);
    Parent::initialize();
  }

  void advance_start() override {
    Parent::advance_start();
    add_event(delay, [](void *data) { static_cast<Blinker *>(data)->blink(); }, this);
  }

  void blink() {
    XGpio_Out32(xil_gpio.BaseAddress + XGPIO_DATA_OFFSET, gpio_flag);
    gpio_flag ^= 0xFFFF;
    add_event(delay, [](void *data) { static_cast<Blinker *>(data)->blink(); }, this);
  }

  void on_blink_command(message::BlinkCommand &msg) {
    blink();
  }

  rl::Duration delay;
};

static void app_hw_init();

using Supervisor =
    rl::Supervisor<rl::SupervisorBase::min_handlers_amount, Blinker>;
using Storage =
    rl::traits::MessageStorage<rl::message::ChangeState,
                               rl::message::ChangeStateAck,
                               message::BlinkCommand>;
using Queue = rl::Queue<Storage, 5>; /* upto 5 messages in 1 queue */
using Planner = rl::Planner<2>;      /* upto 2 time events */

Queue queue;
Planner planner;
rl::Context context{&queue, &planner, &get_now};
Supervisor sup;

int main(int, char **) {
  app_hw_init();

  /* setup */
  sup.bind(context);
  auto blinker = sup.get_child<0>();
  blinker->delay = rl::Duration{1};
  /* let it polls timer */
  sup.start(true);

  /* main cycle */
  sup.process();
  return 0;
}

static void app_hw_init() {
  XGpio_Initialize(&xil_gpio, GPIO_OUTPUT_DEVICE_ID);
  XGpio_SetDataDirection(&xil_gpio, LED_CHANNEL, 0x0);

	microblaze_enable_interrupts();
  enable_timer();
}
